<?php
namespace ITSociaal\Themes\Houtgrutter;

/**
 * @version 0.0.1
 *
 * Contains all kinds of clever uses for call_user_func_array
 * Of course it could not be called apply() because strlen is
 * just the perfect hash function...
 *
 * None of this actually requires a class, or keeps any state
 * at all (i.e. no $this or static vars, all methods declared
 * static). Future versions may simply use a namespace and no
 * class, depending on what works well wrt unit tests.
 */
class F
{
	/**
	 * Create a function which always returns $val
	 *
	 * @param mixed $val
	 *
	 * @return \Closure that returns $val
	 */
	public static function always($val)
	{
		return function() use ($val) {
			return $val;
		};
	}

	/**
	 * In case you're tired of writing call_user_func_array
	 * May be removed in the future, because it's quite meh
	 */
	public static function apply(callable $func, $args=[])
	{
		return call_user_func_array($func, $args);
	}

	/**
	 * @param callable $func
	 *
	 * @return \Closure with the first two parameters swapped
	 *
	 * If a single argument is passed to resulting closure,
	 * the second argument will be substituted with a NULL.
	 */
	public static function flip(callable $func)
	{
		return function() use ($func) {
			return call_user_func_array(
				$func,

				/* Use the fact that arrays are actually
				 * ordered maps to swap just the first 2
				 */
				array_values(array_replace(
					array_reverse(
						array_fill(0, 2, null),
						true
					), // [1] => null, [0] => null
					func_get_args()
				))
			);
		};
	}

	/**
	 * @param callable $func
	 *
	 * @return \Closure with all parameters reversed
	 *
	 * Might not work properly with variable argument lists
	 */
	public static function flipAll(callable $func)
	{
		return function() use ($func) {
			return call_user_func_array(
				$func,
				array_reverse(func_get_args())
			);
		};
	}

	/**
	 * Does nothing but return the given argument
	 *
	 * @param mixed $param
	 *
	 * @return mixed $param
	 */
	public static function identity($param)
	{
		return $param;
	}

	/**
	 * Creates a function that returns the given $property of an object
	 *
	 * @param string $property Name
	 *
	 * @return \Closure Function that returns $property from argument
	 */
	public static function pick($property)
	{
		return function($obj) use ($property) {
			return is_object($obj)
				? $obj->{$property}
				: $obj[$property];
		};
	}

	/**
	 * Creates a function that calls a function depending on a condition
	 *
	 * When called, passes its argument to $test and calls either $onTrue or
	 * $onFalse depending on the return value of $test. $onFalse can be null
	 * and the function will by default simply return the argument.
	 *
	 * @param callable $test    Evaluates its argument and returns bool
	 * @param callable $onTrue  Called with argument in the true case
	 * @param callable $onFalse Called with argument in the false case
	 *
	 * @return \Closure takes one argument
	 */
	public static function conditional($test, $onTrue, $onFalse = null)
	{
		return function($subject) use ($test, $onTrue, $onFalse)
		{
			return call_user_func($test, $subject)
				? call_user_func($onTrue, $subject)
				: (!is_null($onFalse)
					? call_user_func($onFalse, $subject)
					: $subject);
		};
	}
}

/* vi:set ts=4 sw=4 noet: */
